package com.wyh.lotterysystem.service.mq;

import com.wyh.lotterysystem.common.exception.ServiceException;
import com.wyh.lotterysystem.common.utils.JacksonUtil;
import com.wyh.lotterysystem.common.utils.RedisUtil;
import com.wyh.lotterysystem.common.utils.SendMailUtil;
import com.wyh.lotterysystem.controller.request.DrawPrizeRequest;
import com.wyh.lotterysystem.dao.dataobject.*;
import com.wyh.lotterysystem.dao.mapper.*;
import com.wyh.lotterysystem.service.DrawPrizeService;
import com.wyh.lotterysystem.service.activitystatus.ActivityStatusManager;
import com.wyh.lotterysystem.service.dto.ConvertActivityStatusDTO;
import com.wyh.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import com.wyh.lotterysystem.service.enums.ActivityStatusEnum;
import com.wyh.lotterysystem.service.enums.ActivityUserStatusEnum;
import io.jsonwebtoken.lang.Collections;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.Collection;
import java.util.List;
import java.util.Map;

import static com.wyh.lotterysystem.common.config.DirectRabbitConfig.QUEUE_NAME;

@Component
@RabbitListener(queues = QUEUE_NAME)
@Slf4j
public class MqReceiver {

    @Resource(name = "drawPrizeServiceImpl")
    DrawPrizeService drawPrizeService;

    @Resource(name = "activityStatusManagerImpl")
    ActivityStatusManager activityStatusManager;

    @Autowired
    ActivityMapper activityMapper;

    @Autowired
    ActivityUserMapper activityUserMapper;

    @Autowired
    ActivityPrizeMapper activityPrizeMapper;

    @Autowired
    UserMapper userMapper;

    @Autowired
    PrizeMapper prizeMapper;

    @Autowired
    RedisUtil redisUtil;

    @Autowired
    WinnerRecordMapper winnerRecordMapper;

    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    private final String WINNING_RECORDS_PREFIX = "WINNING_RECORDS_";
    private final Long WINNING_RECORDS_TIMEOUT = 60 * 60 * 24 * 2L;

    @RabbitHandler
    public void process(Map<String,  String> message){
          log.info("MQ接收消息成功，message：{}", JacksonUtil.writeValueAsString(message));

          //接收message_data的值
            String paramString = message.get("message_data");
        DrawPrizeRequest request = JacksonUtil.readValue(paramString, DrawPrizeRequest.class);

        try {
            //处理抽奖流程

            //1).校验抽奖信息
            if (!drawPrizeService.checkDrawPrizeRequest(request)){
                return;
            }

            //2).扭转状态
            covertStatus(request);

            //3).保存中奖者名单
            List<WinnerRecordDo> winnerRecordDos = saveWinnerRecord(request);

            //4).通知中奖者
            syncExecute(winnerRecordDos);
        }catch (ServiceException e){
            //如果发生异常就进行回滚
            rollback(request);
            throw e;
        }catch (Exception e){
            rollback(request);
            throw e;
        }
    }

    private void rollback(DrawPrizeRequest request) {
        //有两个事务需要回滚：1.扭转状态；2.保存中奖者记录

        //判断事务1: 奖品层面，活动层面，用户层面
        //handlerEvent方法本身添加了@Transactional注解，
        //如果奖品没有进行回滚，就需要本次回滚，
        //如果奖品回滚了，就不需要本次回滚
        if (covertStatusNeedRollback(request)){
            rollbackCovertStatus(request);
        }else {
            //如果事务1不需要回滚，证明异常一定是事务1抛出的，那么就不会执行到事务2，直接返回
            return;
        }

        //判断事务2是否需要本次回滚
        if (saveWinnerRecordNeedRollback(request)){
            rollbackSaveWinnerRecord(request);
        }
    }

    private void rollbackSaveWinnerRecord(DrawPrizeRequest request) {
        //删除数据库中的数据
        winnerRecordMapper.deleteRecords(request.getActivityId(),request.getPrizeId());

        //删除缓存
        //1.奖品层面
        //2.活动层面
        if (request.getPrizeId() != null){
            redisUtil.delRedis(request.getActivityId() + "_" + request.getPrizeId());
        }

        redisUtil.delRedis(String.valueOf(request.getActivityId()));
    }

    private boolean saveWinnerRecordNeedRollback(DrawPrizeRequest request) {
        int count = winnerRecordMapper.countByAPId(request.getActivityId(),request.getPrizeId());
        return count > 0;
    }

    private void rollbackCovertStatus(DrawPrizeRequest request) {
        ConvertActivityStatusDTO convertActivityStatusDTO = new ConvertActivityStatusDTO();
        convertActivityStatusDTO.setTargetActivityStatus(ActivityStatusEnum.RUNNING);
        convertActivityStatusDTO.setActivityId(request.getActivityId());
        convertActivityStatusDTO.setPrizeId(request.getPrizeId());
        convertActivityStatusDTO.setUserId(
                request.getWinnerList()
                        .stream()
                        .map(DrawPrizeRequest.Winner::getUserId)
                        .toList()
        );
        convertActivityStatusDTO.setTargetActivityUserStatus(ActivityUserStatusEnum.INIT);
        convertActivityStatusDTO.setTargetActivityPrizeStatus(ActivityPrizeStatusEnum.INIT);

        activityStatusManager.rollbackHandlerEvent(convertActivityStatusDTO);
    }


    private boolean covertStatusNeedRollback(DrawPrizeRequest request) {
        ActivityPrizeDo activityPrizeDo = activityPrizeMapper
                .selectByActivityPrizeIdAndActivityId(request.getActivityId(), request.getPrizeId());
        return activityPrizeDo.getStatus().equalsIgnoreCase(ActivityPrizeStatusEnum.COMPLETED.name());
    }

    private void syncExecute(List<WinnerRecordDo> winnerRecordDos) {
        threadPoolTaskExecutor.execute(()->sendMail(winnerRecordDos));
    }

    private void sendMail(List<WinnerRecordDo> winnerRecordDos) {
        for (WinnerRecordDo winnerRecordDo:
             winnerRecordDos) {
            SendMailUtil.sendRecord(winnerRecordDo);
        }
    }

    private List<WinnerRecordDo> saveWinnerRecord(DrawPrizeRequest request) {
        if (null == request) {
            return null;
        }

        //获取活动信息
        ActivityDo activityDo = activityMapper.selectActivityById(request.getActivityId());

        if (activityDo == null){
            return null;
        }

        //获取奖品信息
        ActivityPrizeDo activityPrizeDo = activityPrizeMapper.selectByActivityPrizeIdAndActivityId(request.getActivityId(), request.getPrizeId());
        if (activityPrizeDo == null) {
            return null;
        }

        PrizeDo prizeDo = prizeMapper.select(request.getPrizeId());
        if (prizeDo == null) {
            return null;
        }

        //获取抽奖人员
        List<Integer> winners = request.getWinnerList()
                .stream()
                .map(DrawPrizeRequest.Winner::getUserId)
                .toList();
        List<UserDo> userDos = userMapper.batchSelect(winners);
        if (Collections.isEmpty(userDos)){
            return null;
        }

        List<WinnerRecordDo> winnerRecordDos = userDos.stream()
                .map(userDo -> {
                    WinnerRecordDo winnerRecordDo = new WinnerRecordDo();
                    winnerRecordDo.setActivityId(activityDo.getId());
                    winnerRecordDo.setActivityName(activityDo.getActivityName());
                    winnerRecordDo.setPrizeId(prizeDo.getId());
                    winnerRecordDo.setPrizeName(prizeDo.getName());
                    winnerRecordDo.setPrizeTier(activityPrizeDo.getPrizeTiers());
                    winnerRecordDo.setWinnerId(userDo.getId());
                    winnerRecordDo.setWinnerName(userDo.getUserName());
                    winnerRecordDo.setWinnerEmail(userDo.getEmail());
                    winnerRecordDo.setWinnerPhoneNumber(userDo.getPhoneNumber());
                    winnerRecordDo.setWinningTime(request.getWinningTime());

                    return winnerRecordDo;
                })
                .toList();
        winnerRecordMapper.batchInsert(winnerRecordDos);

        //缓存中奖记录
        cacheWinnerRecord(activityDo.getId() + "_" + prizeDo.getId(),winnerRecordDos,WINNING_RECORDS_TIMEOUT);

        if (activityDo.getStatus().equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())){
            List<WinnerRecordDo> winnerRecordDos1 = winnerRecordMapper.selectByActivityId(request.getActivityId());
            cacheWinnerRecord(String.valueOf(request.getActivityId()),winnerRecordDos1,WINNING_RECORDS_TIMEOUT);
        }

        return winnerRecordDos;
    }

    private void cacheWinnerRecord(String s, List<WinnerRecordDo> winnerRecordDos, Long winning_records_timeout) {
        if (!StringUtils.hasText(s) || CollectionUtils.isEmpty(winnerRecordDos)){
            log.warn("缓存key值或value值为空，缓存失败");
            return;
        }
        String value = JacksonUtil.writeValueAsString(winnerRecordDos);
        redisUtil.setRedisAndTime(s,value,winning_records_timeout);
    }


    private void covertStatus(DrawPrizeRequest request) {
        ConvertActivityStatusDTO activityStatusDTO = new ConvertActivityStatusDTO();
        activityStatusDTO.setActivityId(request.getActivityId());
        activityStatusDTO.setTargetActivityStatus(ActivityStatusEnum.COMPLETED);
        activityStatusDTO.setPrizeId(request.getPrizeId());
        activityStatusDTO.setTargetActivityPrizeStatus(ActivityPrizeStatusEnum.COMPLETED);
        List<Integer> userIds = request.getWinnerList()
                .stream()
                .map(DrawPrizeRequest.Winner::getUserId)
                .toList();
        activityStatusDTO.setUserId(userIds);
        activityStatusDTO.setTargetActivityUserStatus(ActivityUserStatusEnum.COMPLETED);

        activityStatusManager.handlerEvent(activityStatusDTO);
    }

//    List<WinnerRecordDo> getWinnerRecord(){
//
//    }
}
